﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
using LogonDesktopTools;

namespace LogonDesktopToolsService
{
    public partial class LogonDesktopTools : ServiceBase
    {

        readonly string application = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath) + Path.DirectorySeparatorChar + "LogonDesktopTools.exe";
        Thread thread = null;
        bool execute_thread = true;
        ManualResetEvent process_wait_event = new ManualResetEvent(false);
        ManualResetEvent thread_exit_event = new ManualResetEvent(false);
        Process process = null;
        int event_id = 0;
        bool system_locked = true;

        void Log(string message, EventLogEntryType type)
        {
            eventLog.WriteEntry(message, type, event_id++);
        }

        void Log(string message)
        {
            Log(message, EventLogEntryType.Information);
        }

        public LogonDesktopTools()
        {
            InitializeComponent();
            const string log = "Logon Desktop Tools";
            if (!EventLog.SourceExists(log))
                EventLog.CreateEventSource(log, log);
            eventLog.Log = eventLog.Source = log;
            eventLog.Clear();
        }

        void Execute()
        {
            IntPtr token = IntPtr.Zero;
            if (WinApi.OpenProcessToken(Process.GetCurrentProcess().Handle, WinApi.TOKEN_ALL_ACCESS, out token))
            {
                IntPtr duplicated_token = IntPtr.Zero;
                if (WinApi.DuplicateTokenEx(token, 0, IntPtr.Zero, WinApi.SecurityImpersonation, WinApi.TokenPrimary, out duplicated_token))
                {
                    UInt32 session_id = WinApi.WTSGetActiveConsoleSessionId();
                    if (session_id < 0xFFFFFFFF)
                    {
                        if (WinApi.SetTokenInformation(duplicated_token, WinApi.TokenSessionId, ref session_id, sizeof(UInt32)))
                        {
                            WinApi.STARTUPINFO startup_info = new WinApi.STARTUPINFO(true);
                            WinApi.PROCESS_INFORMATION process_info = new WinApi.PROCESS_INFORMATION(true);
                            if (WinApi.CreateProcessAsUser(duplicated_token, null, application + " Winlogon", IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startup_info, ref process_info))
                                process = Process.GetProcessById(process_info.dwProcessId);
                        }
                    }
                    WinApi.CloseHandle(duplicated_token);
                }
                WinApi.CloseHandle(token);
            }
        }

        void OnThread()
        {
            Log("service thread started");
            while (execute_thread)
            {
                if(!system_locked)
                    Execute();
                if (null == process)
                    process_wait_event.WaitOne(TimeSpan.FromSeconds(5));
                else
                {
                    process.WaitForExit();
                    process.Close();
                    process = null;
                    process_wait_event.WaitOne(TimeSpan.FromSeconds(1));
                }
            }
            thread_exit_event.Set();
            Log("service thread stoped");
        }

        protected override void OnStart(string[] args)
        {
            Log("service process started");
            thread = new Thread(new ThreadStart(OnThread));
            thread.Start();
        }

        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            base.OnSessionChange(changeDescription);
            Log("session changed to " + changeDescription.SessionId + " because of " + changeDescription.Reason);
            switch (changeDescription.Reason)
            {
                case SessionChangeReason.SessionLogon:
                case SessionChangeReason.SessionUnlock:
                    system_locked = false;
                    break;
                case SessionChangeReason.SessionLogoff:
                case SessionChangeReason.SessionLock:
                    system_locked = true;
                    if (null != process)
                        process.Kill();
                    break;
            }
        }

        protected override void OnStop()
        {
            execute_thread = false;
            if (thread.IsAlive)
            {
                if (null != process)
                    process.Kill();
                process_wait_event.Set();
                thread_exit_event.WaitOne(TimeSpan.FromSeconds(5));
                thread.Interrupt();
                thread.Abort();
                thread.Join();
            }
            Log("service process stopped");
        }
    }
}
